package com.ifunyoung.realm;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;


public class MyRealm extends AuthorizingRealm {

	/*
		授权方法
		参数principals，理解为登陆用户的username
		返回值为封装了用户的所有角色
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {

		// 1. 获取登陆用户的用户名
		String username = principals.toString();
		SimpleAuthorizationInfo info = null;
		// 2. 查询数据库，获取用户名和密码以及角色
		try {
			Class.forName("com.mysql.jdbc.Driver");
			Connection conn = DriverManager.getConnection("jdbc:mysql:///test2", "root", "root");
			PreparedStatement ps = conn.prepareStatement("select * from user where username = ?");
			ps.setString(1, username);
			ResultSet rs = ps.executeQuery();
			// 3.返回用户在数据库中所拥有的所有角色，这里假设一个用户只有一个角色
			if (rs.next()) {
				// 获取用户的角色
				String role = rs.getString(4);
				Set<String> roles = new HashSet<>();
				roles.add(role);
				
				// 返回info，封装了用户的所有角色，securityManager会对使用了roles过滤器的资源，进行比对，是否拥有对应角色
				info = new SimpleAuthorizationInfo(roles);
				
				//将用户的所有权限添加进去,假设一个用户只有一个权限
				List<String> permissions = new ArrayList<String>();
				permissions.add(rs.getString(5));
				info.addStringPermissions(permissions);
				return info;
			} 
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 查询不到，返回null
		return null;
	}

	/*
		认证方法
		参数token，就是subject.login(token)的token
		返回参数为数据库查询出来的用户名和密码的包装类
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		// 1. 获取登陆用户的用户名
		String username = (String) token.getPrincipal();
		SimpleAuthenticationInfo info = null;
		// 2. 查询数据库，获取用户名和密码
		try {
			Class.forName("com.mysql.jdbc.Driver");
			Connection conn = DriverManager.getConnection("jdbc:mysql:///test2", "root", "root");
			PreparedStatement ps = conn.prepareStatement("select * from user where username = ?");
			ps.setString(1, username);
			ResultSet rs = ps.executeQuery();
			// 3.返回认证信息，交给securityManager进行比对，密码是否一致，不用我们自己手动比对
			if (rs.next()) {
				String db_password = rs.getString(3);
				
				//-------------------------1，明文密码---------------------------------
//				info = new SimpleAuthenticationInfo(username,db_password,this.getName());
				//-------------------------1，明文密码---------------------------------
				
				//-------------------------2，MD5加密密码---------------------------------
				/*
				 * 由于对用户输入的密码做了MD5加密1024次算法，所以现在前端输入的密码为进过加密的密码，数据库存的还是明文123456
				 * 为了通过测试，采用将数据库的明文加密后，进行比较，（本来是应该将加密后的密文存储到数据库中）
				 */
				// 对数据库中取出来的密码进行加密，便于测试通过,这个一般是不用做的
//				SimpleHash sh = new SimpleHash("MD5", db_password, null, 1024);
				
//				info = new SimpleAuthenticationInfo(username,sh,this.getName());
				//-------------------------2，MD5加密密码---------------------------------
				
				//-------------------------3，MD5+salt加密密码---------------------------------
				/*
				 * 在原有加密算法的基础上，再增加salt盐值加密，使得同样的密码，加密后的密码不同
				 */
				//salt加密，使用username作为盐值，此时同样的密码，不同的用户名，就会加密后的结果不同
				ByteSource salt = ByteSource.Util.bytes(rs.getString(2));
				
				//此处参数salt用于对数据库查询出来的密码加密
				SimpleHash sh = new SimpleHash("MD5", db_password, salt, 1024);
				
				//此处参数salt用于对前端token的密码加密
				info = new SimpleAuthenticationInfo(username, sh, salt, this.getName());
				//-------------------------3，MD5+salt加密密码---------------------------------
				
				//返回给securityManager，用于比对token中的密码和info中的密码
				return info;
			} 
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 查询不到，返回null
		return null;
	}


}
